using System.Text;

namespace MagicOnion.Client.SourceGenerator.CodeGen.MemoryPack;

internal class MemoryPackFormatterRegistrationGenerator : ISerializerFormatterGenerator
{
    public (string HintNameSuffix, string Source) Build(GenerationContext generationContext, SerializationFormatterCodeGenContext ctx)
    {
        using var pooledStringBuilder = generationContext.GetPooledStringBuilder();
        var writer = pooledStringBuilder.Instance;

        EmitPreamble(generationContext, ctx, writer);
        EmitBody(generationContext, ctx, writer);
        EmitTypeHints(generationContext, ctx, writer);
        EmitPostscript(generationContext, ctx, writer);

        return (".MemoryPack", writer.ToString());
    }

    static void EmitPreamble(GenerationContext generationContext, SerializationFormatterCodeGenContext ctx, StringBuilder writer)
    {
        writer.AppendLine("""
            // <auto-generated />
            #pragma warning disable

            """);
    }

    static void EmitBody(GenerationContext generationContext, SerializationFormatterCodeGenContext ctx, StringBuilder writer)
    {
        if (!string.IsNullOrEmpty(generationContext.Namespace))
        {
            writer.AppendLineWithFormat($$"""
            namespace {{generationContext.Namespace}}
            {
            """);
        }
        writer.AppendLine($$"""
                using global::System;
                using global::MemoryPack;
            """);

        EmitRegister(generationContext, ctx, writer);

        if (!string.IsNullOrEmpty(generationContext.Namespace))
        {
            writer.AppendLine($$"""
            }
            """);
        }
    }

    static void EmitRegister(GenerationContext generationContext, SerializationFormatterCodeGenContext ctx, StringBuilder writer)
    {
        writer.AppendLineWithFormat($$"""
                partial class {{generationContext.InitializerPartialTypeName}}
                {
                    /// <summary>
                    /// Registers the generated MemoryPackFormatters.
                    /// </summary>
                    public static void RegisterMemoryPackFormatters()
                    {
                        TypeHints.Register();
            """);
        foreach (var (resolverInfo, index) in ctx.FormatterRegistrations.Select((x, i) => (x, i)))
        {
            writer.AppendLineWithFormat($$"""
                        global::MemoryPack.MemoryPackFormatterProvider.Register(new {{(resolverInfo.FormatterName.StartsWith("global::") || string.IsNullOrWhiteSpace(ctx.FormatterNamespace) ? "" : ctx.FormatterNamespace + ".") + resolverInfo.FormatterName}}{{resolverInfo.FormatterConstructorArgs}});
            """);
        }
        writer.AppendLineWithFormat($$"""
                    }
                }
            """);
    }

    static void EmitTypeHints(GenerationContext generationContext, SerializationFormatterCodeGenContext ctx, StringBuilder writer)
    {
        writer.AppendLineWithFormat($$"""
                /// <summary>Type hints for Ahead-of-Time compilation.</summary>
                static class TypeHints
                {
            #if NET8_0_OR_GREATER
            """);
        foreach (var typeHint in ctx.TypeHints.Where(x => !x.FullName.StartsWith("global::System.")))
        {
            writer.AppendLineWithFormat($$"""
                    [System.Diagnostics.CodeAnalysis.DynamicDependency(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicMethods | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.NonPublicMethods, typeof({{typeHint.FullName}}))]
            """);
        }
        writer.AppendLineWithFormat($$"""
            #endif
                    internal static void Register()
                    {
                    }
                }
            """);
    }

    static void EmitPostscript(GenerationContext generationContext, SerializationFormatterCodeGenContext ctx, StringBuilder writer)
    {
    }
}
